﻿using System;
using System.IO;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace Mars.GpuInstance
{
    public readonly struct QuickArray<T> : IDisposable where T : unmanaged
    {
        public readonly T[] managedArray;
        private readonly IntPtr m_Pointer;
        private readonly ulong m_GcHandle;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
        private readonly AtomicSafetyHandle m_Safety;
#endif

        public unsafe QuickArray(T[] array)
        {
            managedArray = array;
            m_Pointer = (IntPtr) UnsafeUtility.PinGCArrayAndGetDataAddress(array, out m_GcHandle);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            m_Safety = AtomicSafetyHandle.Create();
#endif
        }

        public NativeArray<T> GetNativeArray()
        {
            return GetNativeArray(0, managedArray.Length);
        }

        public unsafe T* GetPointer()
        {
            return (T*) m_Pointer;
        }

        public unsafe NativeArray<T> GetNativeArray(int offset, int length)
        {
            var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(
                (T*) m_Pointer + offset, length, Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, m_Safety);
#endif
            return nativeArray;
        }

        public unsafe NativeArray<T1> GetNativeArray<T1>(int byteOffset, int elementCount) where T1 : unmanaged
        {
            var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T1>(
                (byte*) m_Pointer + byteOffset, elementCount, Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, m_Safety);
#endif
            return nativeArray;
        }

        public unsafe UnmanagedMemoryStream GetUnmanagedMemoryStream()
        {
            return new UnmanagedMemoryStream((byte*) m_Pointer, sizeof(T) * managedArray.Length);
        }

        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckDeallocateAndThrow(m_Safety);
            AtomicSafetyHandle.Release(m_Safety);
#endif
            UnsafeUtility.ReleaseGCObject(m_GcHandle);
        }
    }
}